﻿#coding=utf-8
#!/usr/bin/python
import re
import json
import time
import hashlib
import quickjs
import requests
from base64 import b64decode
from urllib.parse import unquote
from difflib import SequenceMatcher
from concurrent.futures import ThreadPoolExecutor, as_completed
from units import removeEmoji, removeHtmlTags, getCache, setCache, delCache

class LIVE():  # 元类 默认的元类 type
	def getInfo(self, params):
		self.extend = params['extend']
		if params['wd'] != '':
			return self.searchContent(params['wd'], params['pg'], params['quick'])
		elif params['t'] != '':
			return self.categoryContent(params['t'], params['pg'], json.loads(b64decode(unquote(params['ext'])).decode()))
		elif params['ids'] != '':
			return self.detailContent(params['ids'])
		elif params['play'] != '':
			return self.playerContent(params['flag'], params['play'])
		else:
			return self.homeContent(params['filter'])

	def homeContent(self, filter):
		result = {}
		try:
			url = json.loads(self.extend)['url']
			r = requests.get(url, headers=self.header, timeout=5)
			data = r.json()
			result['class'] = data['classes']
			if filter:
				result['filters'] = data['filter']
		except:
			result['class'] = [{"type_id": 'douyu', "type_name": "斗鱼"}]
			result['filters'] = {'douyu': {'key': '斗鱼', 'name': '斗鱼', "value": [{"n": "一起看", "v": "208"}]}}
		return result, 1

	def categoryContent(self, tid, pg, ext):
		result = {}
		videos = []
		header = self.header.copy()
		if tid == 'bilibili':
			url = 'https://api.live.bilibili.com/xlive/web-interface/v1/second/getList'
			if 'B站' in ext:
				cid = ext['B站']
			else:
				try:
					r = requests.get(json.loads(self.extend)['url'], headers=header, timeout=5)
					cid = r.json()['filter'][tid][0]['value'][0]['v']
				except:
					cid = '1'
			params = {
				'platform': 'web',
				'parent_area_id': cid,
				'page': pg,
			}
			r = requests.get(url, headers=header, params=params, timeout=5)
			data = r.json()
			vodList = data['data']['list']
			pf = 'bilibili'
			imgKey = 'cover'
			vidKey = 'roomid'
			titleKey = 'title'
			remarkKey = 'uname'
			if data['data']['has_more'] == 1:
				pageCount = str(int(pg) + 1)
			else:
				pageCount = pg
		elif tid == 'douyu':
			if '斗鱼' in ext:
				cid = ext['斗鱼']
			else:
				try:
					r = requests.get(json.loads(self.extend)['url'], headers=header, timeout=5)
					cid = r.json()['filter'][tid][0]['value'][0]['v']
				except:
					cid = '208'
			url = 'https://www.douyu.com/gapi/rkc/directory/mixList/2_{}/{}'.format(cid, pg)
			r = requests.get(url, headers=header, timeout=5)
			data = r.json()
			vodList = data['data']['rl']
			pageCount = data['data']['pgcnt']
			pf = 'douyu'
			imgKey = 'rs1'
			vidKey = 'rid'
			titleKey = 'rn'
			remarkKey = 'nn'
		elif tid == 'huya':
			if '虎牙' in ext:
				cid = ext['虎牙']
			else:
				try:
					r = requests.get(json.loads(self.extend)['url'], headers=header, timeout=5)
					cid = r.json()['filter'][tid][0]['value'][0]['v']
				except:
					cid = '2135'
			header['Referer'] = 'https://www.huya.com/'
			url = 'https://www.huya.com/cache.php?m=LiveList&do=getLiveListByPage&gameId={}&tagAll=0&callback=getLiveListJsonpCallback&page={}'.format(cid, pg)
			r = requests.get(url, headers=header, timeout=5)
			data = json.loads(re.search(r"getLiveListJsonpCallback\((.*)\)", r.text).group(1))
			vodList = data['data']['datas']
			pageCount = data['data']['totalPage']
			pf = 'huya'
			imgKey = 'screenshot'
			vidKey = 'profileRoom'
			titleKey = 'introduction'
			remarkKey = 'nick'
		else:
			vodList = []
			pf = ''
			imgKey = ''
			vidKey = ''
			titleKey = ''
			remarkKey = ''
			pageCount = pg
		for vod in vodList:
			img = vod[imgKey]
			vid = vod[vidKey]
			title = vod[titleKey]
			remark = vod[remarkKey]
			videos.append({
				"vod_id": f'{{\"name\": \"{removeEmoji(title)}\", \"pf\": \"{pf}\", \"vid\": \"{str(vid)}\"}}',
				"vod_name": removeEmoji(title),
				"vod_pic": removeEmoji(img),
				"vod_remarks": removeEmoji(remark)
			})
		lenvodList = len(vodList)
		result['list'] = videos
		result['page'] = pg
		result['pagecount'] = pageCount
		result['limit'] = lenvodList
		result['total'] = lenvodList
		return result, 600

	def detailContent(self, ids):
		params = json.loads(ids)
		header = self.header.copy()
		pf = params['pf']
		vid = params['vid']
		name = params['name']
		if pf == 'bilibili':
			url = f'https://api.live.bilibili.com/room/v1/Room/playUrl?cid={vid}&qn=20000&platform=h5'
			r = requests.get(url, headers=header, timeout=5)
			data = r.json()
			platFormList = ['线路1']
			playUrlList = [data['data']['quality_description'][0]['desc'] + '$' + data['data']['durl'][0]['url']]
		elif pf == 'douyu':
			try:
				did = '10000000000000000000000000001501'
				t = str(int(time.time()))
				session = requests.Session()
				rid = str(vid)
				r = session.get('https://m.douyu.com/' + rid, headers=header, timeout=5).text
				result = re.search(r'(function ub98484234.*)\s(var.*)', r).group()
				funcUb9 = re.sub(r'eval.*;}', 'strc;}', result)
				jsFunc = quickjs.Function('ub98484234', funcUb9)
				res = jsFunc()
				v = re.search(r'v=(\d+)', res).group(1)
				rb = hashlib.md5((rid + did + t + v).encode('utf-8')).hexdigest()
				funcSign = re.sub(r'return rt;}\);?', 'return rt;}', res)
				funcSign = funcSign.replace('(function (', 'function sign(')
				funcSign = funcSign.replace('CryptoJS.MD5(cb).toString()', '"' + rb + '"')
				jsFunc = quickjs.Function('sign', funcSign)
				params = jsFunc(rid, did, t)
				params += '&ver=219032101&rid={}&rate=-1'.format(rid)
				url = 'https://m.douyu.com/api/room/ratestream'
				r = session.post(url, params=params, headers=header, timeout=5)
				session.close()
				jo = json.loads(r.text)['data']
				platFormList = ['线路1']
				playUrlList = ['直播$'+jo['url']]
			except:
				return {}, 1
		elif pf == 'huya':
			import html
			header['Content-Type'] = 'application/x-www-form-urlencoded'
			url = f'https://www.huya.com/{vid}'
			r = requests.get(url, headers=header, timeout=5)
			try:
				data = json.loads(re.search(r'stream: ([\s\S]*?)\n', r.text).group(1))
			except:
				data = json.loads(b64decode(re.search(r'"stream": "([\s\S]*?)"', r.text).group(1)).decode())
			platFormList = []
			playUrlList = []
			i = 1
			for pL in data['data'][0]['gameStreamInfoList']:
				platFormList.append('线路{}'.format(str(i)))
				baseurl = pL['sHlsUrl'] + '/' + pL['sStreamName'] + '.' + pL['sHlsUrlSuffix']
				srcAntiCode = html.unescape(pL['sHlsAntiCode'])
				c = srcAntiCode.split('&')
				c = [i for i in c if i != '']
				n = {i.split('=')[0]: i.split('=')[1] for i in c}
				fm = unquote(n['fm'])
				u = b64decode(fm).decode('utf-8')
				hash_prefix = u.split('_')[0]
				ctype = n.get('ctype', '')
				txyp = n.get('txyp', '')
				fs = n.get('fs', '')
				t = n.get('t', '')
				seqid = str(int(time.time() * 1e3 + 1463993859134))
				wsTime = hex(int(time.time()) + 3600).replace('0x', '')
				hash = hashlib.md5('_'.join([hash_prefix, '1463993859134', pL['sStreamName'], hashlib.md5((seqid + '|' + ctype + '|' + t).encode('utf-8')).hexdigest(), wsTime]).encode('utf-8')).hexdigest()
				ratio = ''
				purl = "{}?wsSecret={}&wsTime={}&seqid={}&ctype={}&ver=1&txyp={}&fs={}&ratio={}&u={}&t={}&sv=2107230339".format(baseurl, hash, wsTime, seqid, ctype, txyp, fs, ratio, '1463993859134', t)
				playUrlList.append('直播$' + purl)
				i += 1
		else:
			platFormList = []
			playUrlList = []
		vod = {"vod_id": vid, "vod_name": name}
		vod['vod_play_from'] = '$$$'.join(platFormList)
		vod['vod_play_url'] = '$$$'.join(playUrlList)
		result = {
			'list': [vod]}
		return result, 600

	def searchContent(self, key, pg, quick):
		items = []
		keyword = key
		if pg == 1:
			siteList = ['bb', 'dy', 'hy']
		else:
			siteList = getCache('livesiteList_{}_{}'.format(keyword, pg))
			delCache('livesiteList_{}_{}'.format(keyword, pg))
			if not siteList:
				return {'list': items}, 600
		contents = []
		with ThreadPoolExecutor(max_workers=3) as executor:
			searchList = []
			try:
				for site in siteList:
					tag = site
					api = ''
					params = {"api": api, "tag": tag, "key": key, "pg": pg}
					future = executor.submit(self.runSearch, params)
					searchList.append(future)
				for future in as_completed(searchList, timeout=30):
					contents.append(future.result())
			except:
				executor.shutdown(wait=False)
		nextpageList = []
		for content in contents:
			if content is None:
				continue
			key = list(content.keys())[0]
			infos = content[key]
			items = items + content[key][0]
			nextpageList.append(infos[1])
			if not infos[1]:
				siteList.remove(key)
		setCache('livesiteList_{}_{}'.format(keyword, pg+1), siteList)
		result = {
			'list': items
		}
		return result, 600

	def runSearch(self, params):
		try:
			tag = params['tag']
			funList = dir(LIVE)
			defname = 'self.search' + tag
			if defname.replace('self.', '') in funList and tag != '':
				result = eval(defname)(params)
				return result
		except:
			pass

	def searchbb(self, params):
		items = []
		pg = params['pg']
		key = params['key']
		tag = params['tag']
		header = self.header.copy()
		if key.isdigit():
			url = f'https://api.live.bilibili.com/room/v1/Room/get_info?room_id={key}'
			r = requests.get(url, headers=header, timeout=5)
			data = r.json()
			if data['data']['live_status'] != 0:
				title = data['data']['title']
				items.append({
					'vod_id': f'{{\"name\": \"{removeEmoji(title)}\", \"pf\": \"bilibili\", \"vid\": \"{str(key)}\"}}',
					'vod_name': title,
					'vod_pic': data['data']['keyframe'],
					"vod_remarks": 'B站直播'
				})
		header['Cookie'] = 'buvid3=0'
		params = {
			'page': pg,
			'page_size': 10,
			'order': 'online',
			'search_type': 'live_user',
			'keyword': key
		}
		r = requests.get('https://api.bilibili.com/x/web-interface/search/type', params=params, headers=header, timeout=5)
		data = r.json()
		vList = data['data']['result']
		for video in vList:
			if video['live_status'] == 0:
				continue
			title = removeHtmlTags(video['uname'])
			if SequenceMatcher(None, title, key).ratio() < 0.6 and key not in title:
				continue
			items.append({
				'vod_id': f'{{\"name\": \"{removeEmoji(title)}\", \"pf\": \"bilibili\", \"vid\": \"{str(video["roomid"])}\"}}',
				'vod_name': title,
				'vod_pic': 'https:' + video['uface'],
				"vod_remarks": 'B站直播'
			})
		return {tag: [items, pg * 10 < len(items)]}

	def searchdy(self, params):
		items = []
		pg = params['pg']
		key = params['key']
		tag = params['tag']
		header = self.header.copy()
		if key.isdigit():
			r = requests.get(f'https://m.douyu.com/{key}', headers=header, timeout=5)
			data = json.loads(re.search(r'\"application/json\">(.*?)</script>', r.text).group(1))
			if data['pageProps']['room']['roomInfo']['roomInfo']['isLive'] == 1:
				title = data['pageProps']['room']['roomInfo']['roomInfo']['roomName']
				items.append({
					'vod_id': f'{{\"name\": \"{removeEmoji(title)}\", \"pf\": \"douyu\", \"vid\": \"{str(key)}\"}}',
					'vod_name': title,
					'vod_pic': data['pageProps']['room']['roomInfo']['roomInfo']['roomSrc'],
					"vod_remarks": '斗鱼直播'
				})
		params = {
			'kw': key,
			'page': pg,
			'pageSize': 10,
			'filterType': 1
		}
		r = requests.get('https://www.douyu.com/japi/search/api/searchUser', params=params, headers=header, timeout=5)
		data = r.json()
		vList = data['data']['relateUser']
		for video in vList:
			if video['anchorInfo']['isLive'] != 1:
				continue
			title = removeHtmlTags(video['anchorInfo']['nickName'])
			if SequenceMatcher(None, title, key).ratio() < 0.6 and key not in title:
				continue
			items.append({
				'vod_id': f'{{\"name\": \"{removeEmoji(title)}\", \"pf\": \"douyu\", \"vid\": \"{str(video["anchorInfo"]["rid"])}\"}}',
				'vod_name': title,
				'vod_pic': video['anchorInfo']['roomSrc'],
				"vod_remarks": '斗鱼直播'
			})
		return {tag: [items, pg * 10 < len(items)]}

	def searchhy(self, params):
		items = []
		pg = params['pg']
		key = params['key']
		tag = params['tag']
		header = self.header.copy()
		if key.isdigit():
			header['Content-Type'] = 'application/x-www-form-urlencoded'
			url = f'https://m.huya.com/{key}'
			r = requests.get(url, headers=header, timeout=5)
			try:
				data = json.loads(re.search(r'stream: ([\s\S]*?)\n', r.text).group(1))
			except:
				data = json.loads(b64decode(re.search(r'"stream": "([\s\S]*?)"', r.text).group(1)).decode())
			title = data['data'][0]['gameLiveInfo']['roomName']
			items.append({
				'vod_id': f'{{\"name\": \"{removeEmoji(title)}\", \"pf\": \"huya\", \"vid\": \"{str(key)}\"}}',
				'vod_name': title,
				'vod_pic': data['data'][0]['gameLiveInfo']['screenshot'],
				"vod_remarks": '虎牙直播'
			})
		header['Cookie'] = 'buvid3=0'
		params = {
			'm': 'Search',
			'do': 'getSearchContent',
			'typ': -5,
			'livestate': 1,
			'q': key,
			'start': (pg - 1) * 40,
			'rows': 40
		}
		r = requests.get('https://search.cdn.huya.com/', params=params, headers=header, timeout=5)
		data = r.json()
		vList = data['response']['1']['docs']
		for video in vList:
			title = removeHtmlTags(video['game_nick'])
			if SequenceMatcher(None, title, key).ratio() < 0.6 and key not in title:
				continue
			items.append({
				'vod_id': f'{{\"name\": \"{removeEmoji(title)}\", \"pf\": \"huya\", \"vid\": \"{str(video["room_id"])}\"}}',
				'vod_name': title,
				'vod_pic': video['game_avatarUrl180'],
				"vod_remarks": '虎牙直播'
			})
		return {tag: [items, pg * 40 < len(items)]}

	def playerContent(self, flag, pid):
		result = {}
		header = self.header.copy()
		result["parse"] = 0
		result["playUrl"] = ''
		result["url"] = pid
		result["header"] = header
		return result, 14400

	header = {
		"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36"
	}
